package com.googlecode.beanfiles.translators;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.googlecode.beanfiles.TranslationException;
import com.googlecode.beanfiles.TranslationNotice;

/**
 * A translator that works with multiple column indexes to perform a property translation.
 */
public class IndexedRangeTranslator extends IndexedTranslator {

    protected static Log log() {
        return LogFactory.getLog(IndexedRangeTranslator.class);
    }

    private Map<String, Set<Integer>> nullIndexes = new HashMap<String, Set<Integer>>();

    public IndexedRangeTranslator() { }
    
    /**
     * Subclasses and package-mates only.
     */
    protected IndexedRangeTranslator(String propertyName) {
        super(propertyName);
    }
    
    /**
     * Constructs a translator that operates on the specified indexes.
     * @param propertyName
     * @param indexes
     */
    public IndexedRangeTranslator(String propertyName, List<Integer> indexes) {
        super(propertyName);
        this.indexes = indexes;
    }

    /**
     * Constructs a translator that operates on a sequential range of values, start..end.
     * 
     * Note that the start and end values are inclusive.
     * 
     * @param start
     * @param end
     */
    public IndexedRangeTranslator(String propertyName, int start, int end) {
        super(propertyName);
        if (end < start || start < 0 || end < 0) {
            throw new TranslationException("Illegal start and end combination: start: " + start + " end: " + end);
        }
        for (int indexValue = start; indexValue <= end; indexValue++) {
            indexes.add(indexValue);
        }
    }

    protected void setIndexes(List<Integer> indexes) {
        this.indexes.addAll(indexes);
    }

    /**
     * Copies values from indexes in an Array or List into a new List for return. Useful for
     * grabbing values at indexes, e.g., 1,4,6 or 1..5 and putting them into a single list property.
     * 
     * Strings are trimmed to null, and nulls are not added to the returned List.
     * 
     * TranslationNotice instances are added for indexes containing empty strings or nulls.
     */
    @Override
    @SuppressWarnings("unchecked")
    public Object read(Object i, Class<?> clazz) {
        List list = new ArrayList();
        if (indexes != null) {
            for (int index : indexes) {
                Object value = ((List) i).get(index);

                if (value instanceof String) {
                    value = StringUtils.trimToNull((String) value);
                }
                if (value != null) {
                    list.add(value);
                } else {
                    addNullIndex(index, null);
                }
            }
            addNullNotices();
        }
        return list;
    }

    protected void addNullIndex(int index, String propertyName) {
        Set<Integer> indexes = nullIndexes.get(propertyName);
        if (indexes == null) {
            indexes = new HashSet<Integer>();
            nullIndexes.put(propertyName, indexes);
        }
        indexes.add(index);
    }

    protected void addNullNotices() {
        for (String propertyName : nullIndexes.keySet()) {
            for (int index : nullIndexes.get(propertyName)) {
                TranslationNotice notice = new TranslationNotice(index + 1);
                notice.setPropertyName(propertyName);
                addTranslationNotice(notice);
            }
        }
    }

    @Override
    public Object write(Object t) {
        throw new TranslationException("not supported");
    }

    public Map<String, Set<Integer>> getNullIndexes() {
        return nullIndexes;
    }

}
